package sg.nus.iss.vmcs.controllers;

import sg.nus.iss.vmcs.VMCSException;
import sg.nus.iss.vmcs.dao.VmcsDAOFactory;
import sg.nus.iss.vmcs.gui.GUIChangeManager;
import sg.nus.iss.vmcs.gui.GUIChangeManagerEvent;
import sg.nus.iss.vmcs.models.CashStore;
import sg.nus.iss.vmcs.models.CashStoreItem;
import sg.nus.iss.vmcs.models.Coin;
import sg.nus.iss.vmcs.models.DrinksBrand;
import sg.nus.iss.vmcs.models.DrinksStore;
import sg.nus.iss.vmcs.models.DrinksStoreItem;
import sg.nus.iss.vmcs.models.Store;
import sg.nus.iss.vmcs.models.StoreItem;
import sg.nus.iss.vmcs.utils.StoreType;

/**
 * Store Controller for controlling the Store Related Operations<br><br>
 * Modifications:
 * <li>	
 * 		28/12/2010 - Philip - Modified class to only have store specific operations. 
 * 		Machinery operations shifted to machinery controller
 * 		01/01/2011 - Seng Chia How - Modified class to use DAO to access and save to
 *      persistent source, and shift out the corresponding responsibility.
 * </li>
 * <br><br>
 * @author Olivo Miotto
 * @author Philip Chew
 */
public class StoreController {
	
    /** Reference to the main controller */
    private MainController mCtrl;
    
	/** Cash Store */
    private CashStore cStore;
    
    /** Drinks Store */
    private DrinksStore dStore;
	
	/**
	 * Creates an instance of the store controller
	 * @param cashPropLoader Property loader for loading of Cash Properties
	 */
	public StoreController(MainController mCtrl) {
		try {
			VmcsDAOFactory daoFactory = VmcsDAOFactory.getDAOFactory(VmcsDAOFactory.StorageType.JAVAPROPERTIES);
			this.dStore = daoFactory.getStoreDAO().getDrinksStore();
			this.cStore = daoFactory.getStoreDAO().getCashStore();
		} 
		catch (VMCSException e) {
			System.out.println("Exception in Store Controller: " + e.getStackTrace());
		}
		this.mCtrl = mCtrl;
	}
	
	/**
	 * Returns the reference to the MainController
	 * @return Reference to the MainController
	 */
	public MainController getMainController() {
		return mCtrl;
	}
	
	/**
	 * Instructs a particular store to update the quantity for a particular store item
	 * @param storeType Type of Store
	 * @param storeItemId ID of the Item in the store
	 * @param value Value to update to
	 */
	public void changeStoreQuantity(StoreType storeType, int storeItemId, int value) {
		try {
			switch (storeType){
				case CASH:
					cStore.setQuantity(storeItemId, value);
					break;
				case DRINKS:	
					dStore.setQuantity(storeItemId, value);
			        break;
			}
		} catch (VMCSException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Closes the store management functionality of the vending machine.
	 * This saves all values to the property files
	 */
	public void closeDown() throws VMCSException{
		saveCashProperty();
		saveDrinksProperty();
	}
	
	/**
	 * Instructs the drink store to dispense one drink then updates the machinery simulator panel
	 * @param drinkBrand Brand of drink to dispense
	 * @return True if dispensing if successful, false if otherwise
	 */
	public boolean dispenseDrink(int drinkBrand) {
		try {
			StoreItem drink = dStore.getStoreItem(drinkBrand);
					
			if (drink != null){
				int qty = drink.getQuantity();
				if (qty != 0)
				{
					drink.setQuantity(qty - 1);
					GUIChangeManager.getInstance().fireEvent(GUIChangeManagerEvent.SetCan, drink.getContent().getName());
					return true;
				}				
			}
		} catch (VMCSException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	/**
	 * Gets the total cash held in the cash store
	 * @return Total Cash
	 */
	public int getTotalCash() {
		int totalValue = 0;
		try {
			for( int i = 0; i < cStore.getStoreSize(); i++){
				int quantity =  cStore.getStoreItem(i).getQuantity();
				Coin c = (Coin)cStore.getStoreItem(i).getContent();
				totalValue += c.getValue() * quantity;
		}
			return totalValue;
		} catch (VMCSException e) {
			e.printStackTrace();
		}
		return 0;	    
	}
	
	/**
	 * Instructs the Cash Store to issue a number of coins of a specific denomination
	 * @param coinIndex Index of coin denomination to return
	 * @param numCoins Number of coins to return
	 * @return True if change is given, false if otherwise
	 */
	public boolean giveChange(int  coinIndex, int numCoins){
		try {
			StoreItem coin = dStore.getStoreItem(coinIndex);
			if (coin != null){
				int qty = coin.getQuantity();
				if (qty >= numCoins)
				{
					coin.setQuantity(qty - numCoins);
					return true;
				}
			}
		} catch (VMCSException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	/**
	 * Used to set the price of a drink brand as determined by the maintainer
	 * @param drinkIndex The index of the drink to set
	 * @param price The new price to set to
	 */
	public void setPrice(int drinkIndex, int price) {
		try {
			DrinksStoreItem item = (DrinksStoreItem )dStore.getStoreItem(drinkIndex);
			DrinksBrand bd = (DrinksBrand) item.getContent();
			String brandName = bd.getName();
			
			  //Must set the price for all drinks in the store with same brand
		      for(int i = 0; i < dStore.getStoreSize(); i++){
		    	  DrinksBrand currBrand = (DrinksBrand) dStore.getStoreItem(i).getContent();
		    	  String currBrandName = dStore.getStoreItem(i).getContent().getName();
		    	  if(currBrandName.equals(brandName))
		    	  {
		    		  currBrand.setPrice(price);
		    	  }
		      }
		} catch (VMCSException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Instructs the Cash Store to store the coin sent as input and updates the display on the machinery simulator panel
	 * @param coin Coin to store
	 * @return True if successful, false if otherwise
	 */
	public boolean storeCoin(Coin coin) {
		StoreItem[] items = cStore.getItems();
	      for(int i = 0; i < cStore.getStoreSize(); i++){
	    	  if(items[i].getContent().getName().equals(coin.getName())) {
	    		  items[i].setQuantity(items[i].getQuantity() + 1);
	    		  return true;
	    	  }
	      }	
	      
		return false;
	}
	
	/**
	 * Transfers all the cash from cash store to the maintainer 
	 * @return The total cash in the cash store
	 */
	public int transferAll() {
		try {
	      int  cc = 0;  // coin quantity;
	      int  size = cStore.getStoreSize();

	      CashStoreItem  item;
	      for(int i = 0; i < size; i++){
	        item = (CashStoreItem )cStore.getStoreItem(i);
	        cc = cc + item.getQuantity();
	        item.setQuantity(0);
	      }
	      return cc;
		} catch (VMCSException e) {
			e.printStackTrace();
		}
		return 0;
	}
	
	/**
	 * Returns a store of a specific type
	 * @return Type of store requested
	 */
	public Store getStore(StoreType type) {
		switch (type){
			case CASH:
				return cStore;
			case DRINKS:
				return dStore;
			default:
				return null;
		}
	}
	
	/**
	 * Saves Cash property to the input file
	 */
	private void saveCashProperty() {
		VmcsDAOFactory daoFactory = VmcsDAOFactory.getDAOFactory(VmcsDAOFactory.StorageType.JAVAPROPERTIES);
		try {
			daoFactory.getStoreDAO().saveCashStore(this.cStore);
		} catch (VMCSException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Saves the Drinks property to the input file
	 */
	private void saveDrinksProperty() {
		VmcsDAOFactory daoFactory = VmcsDAOFactory.getDAOFactory(VmcsDAOFactory.StorageType.JAVAPROPERTIES);
		try {
			daoFactory.getStoreDAO().saveDrinksStore(this.dStore);
		} catch (VMCSException e) {
			e.printStackTrace();
		}
	}
}
